﻿// FingerDlg.cpp : 实现文件
#include "stdafx.h"
#include "Finger.h"
#include "FingerDlg.h"
#include "afxdialogex.h"
#include<io.h>
#include<iostream>
#include<iomanip>
#include<fstream>
#include<Windows.h>

#define EMPTY "empty.bmp"//空白图
#define STEP_IMG_1 "temp\\Step1_Source.bmp"
#define STEP_TXT_1 "temp\\Step1_Source.txt"
#define STEP_TXT_2 "temp\\Step2_MidFilter.txt"
#define STEP_IMG_2 "temp\\Step2_MidFilter.bmp"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CFingerDlg 对话框

CFingerDlg::CFingerDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(IDD_FINGER_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

//P47：多字节字符串转换为Unicode宽字符串  
wchar_t *ToWideChar(char *str)
{
	int num = MultiByteToWideChar(0, 0, str, -1, NULL, 0);

	wchar_t *wideStr = new wchar_t[num];

	MultiByteToWideChar(0, 0, str, -1, wideStr, num);

	return wideStr;
}

//删除文件夹
bool DeleteF(const TCHAR * sPath, bool bDelDir)
{
	CFileFind ff;
	BOOL bFound;
	bFound = ff.FindFile(CString(sPath) + "\\*.*");
	while (bFound)
	{
		bFound = ff.FindNextFile();
		CString sFilePath = ff.GetFilePath();

		if (ff.IsDirectory())
		{
			if (!ff.IsDots())
			{
				DeleteF(sFilePath, bDelDir);
			}
		}
		else
		{
			if (ff.IsReadOnly())
			{
				SetFileAttributes(sFilePath, FILE_ATTRIBUTE_NORMAL);
			}
			DeleteFile(sFilePath);
		}
	}
	ff.Close();

	if (bDelDir)//是否删除该文件夹
	{
		SetFileAttributes(sPath, FILE_ATTRIBUTE_NORMAL);  //设置文件夹的属性  
		RemoveDirectory(sPath);
	}
	return true;
}


//点击关闭窗口自动删除文件夹
CFingerDlg::~CFingerDlg()
{
	CDialog::OnDestroy();

	CString path1 = ToWideChar("Database\\");
	CString path2 = ToWideChar("temp\\");
	CString path3 = ToWideChar("capt.bmp");

	DeleteF(path1,true);
	DeleteF(path2,true);
	DeleteFile(path3);
}

//P42: 绑定控件
void CFingerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_STATIC_INFO, m_staticInfo);
	DDX_Control(pDX, IDC_STATIC_IMG_1, m_picCtrl1);
	DDX_Control(pDX, IDC_STATIC_IMG_2, m_picCtrl2);
	DDX_Control(pDX, IDC_STATIC_IMG_3, m_picCtrl3);
	DDX_Control(pDX, IDC_STATIC_IMG_4, m_picCtrl4);
	DDX_Control(pDX, IDC_STATIC_IMG_5, m_picCtrl5);
	DDX_Control(pDX, IDC_STATIC_IMG_6, m_picCtrl6);
	DDX_Control(pDX, IDC_STATIC_IMG_7, m_picCtrl7);
	DDX_Control(pDX, IDC_STATIC_IMG_8, m_picCtrl8);
	DDX_Control(pDX, IDC_STATIC_IMG_9, m_picCtrl9);
	DDX_Control(pDX, IDC_STATIC_IMG_10, m_picCtrl10);
	DDX_Control(pDX, IDC_STATIC_IMG_11, m_picCtrl11);
	DDX_Control(pDX, IDC_STATIC_IMG_13, m_picCtrl13);

	DDX_Text(pDX, IDC_EDIT_NAME, m_name);
	DDX_Text(pDX, IDC_EDIT_NAME1, m_name1);
	DDX_Control(pDX, IDC_ZKFPENGX1, m_zkfpEng);
}

//按钮触发事件
BEGIN_MESSAGE_MAP(CFingerDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BTN_EXIT, &CFingerDlg::OnBnClickedBtnExit)
	ON_BN_CLICKED(IDC_BTN_CONNECT, &CFingerDlg::OnBnClickedBtnConnect)
	ON_BN_CLICKED(IDC_DISCONNECT, &CFingerDlg::OnBnClickedDisconnect)
	ON_BN_CLICKED(IDC_BTN_CAPTURE, &CFingerDlg::OnBnClickedBtnCapture)
	ON_BN_CLICKED(IDC_BTN_STEP_1, &CFingerDlg::OnBnClickedBtnStep1)
	ON_BN_CLICKED(IDC_BTN_DATABASE, &CFingerDlg::OnBnClickedBtnDatabase)
	ON_BN_CLICKED(IDC_BUTTON2, &CFingerDlg::OnBnClickedButton2)
END_MESSAGE_MAP()


BEGIN_EVENTSINK_MAP(CFingerDlg,CDialog)
	ON_EVENT(CFingerDlg,IDC_ZKFPENGX1,8,OnImageReceivedZkfpengx,VTS_PBOOL)
	END_EVENTSINK_MAP()

// CFingerDlg 消息处理程序

BOOL CFingerDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时，框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	InitDatabase();//创建并初始化指纹库P63

	return TRUE;  // 除非将焦点设置到控件，否则返回 TRUE
}

// 如果向对话框添加最小化按钮，则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序，
//  这将由框架自动完成。

void CFingerDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CFingerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


//P46: 在控件中显示图像
int ShowImageInCtrl(CStatic& picCtrl, char* filename)
{
	//载入图像
	CImage image;
	HRESULT hResult = image.Load(ToWideChar(filename));

	//获得图像参数
	long width = image.GetWidth();
	long height = image.GetHeight();

	//设置显示区域
	CRect rect;
	picCtrl.GetClientRect(&rect);
	CDC* pDc = picCtrl.GetWindowDC();
	SetStretchBltMode(pDc->m_hDC, STRETCH_HALFTONE);


	//显示图像
	image.StretchBlt(pDc->m_hDC, rect, SRCCOPY);

	//更新控件显示
	picCtrl.Invalidate(false);

	//释放变量空间
	image.Destroy();

	picCtrl.ReleaseDC(pDc);

	return 0;
}

//P43: 退出事件；
void CFingerDlg::OnBnClickedBtnExit()
{
	OnOK();//关闭当前对话框
}

//P44: 启动事件
void CFingerDlg::OnBnClickedBtnConnect()
{
	long z = m_zkfpEng.InitEngine();
	if (z == 0) {
		ShowImageInCtrl(m_picCtrl1, EMPTY);//设置成空白图
		ShowImageInCtrl(m_picCtrl2, EMPTY);//设置成空白图
		MessageBox(_T("采集器连接成功."), _T("提示"));//显示信息	
	}
	else if (z == 1) {
		MessageBox(_T("指纹识别驱动程序加载失败."), _T("提示"));//显示信息
	}
	else if (z == 2) {
		MessageBox(_T("没有连接指纹识别仪."), _T("提示"));
	}
	GetDlgItem(IDC_BTN_CONNECT)->EnableWindow(FALSE);//点击启动后，启动按钮灰色，无法再点击。
}

//P45: 接受图像响应事件函数
void CFingerDlg::OnImageReceivedZkfpengx(BOOL FAR* AImageValid)
{
	HDC hdc = this->GetDC()->m_hDC;

	CRect rect, rect1;
	GetDlgItem(IDC_STATIC_IMG_1)->GetClientRect(&rect);
	long width = rect.Width();
	long height = rect.Height();

	GetDlgItem(IDC_STATIC_IMG_1)->GetWindowRect(&rect1);
	ScreenToClient(&rect1);
	int x = rect1.left;
	int y = rect1.top;

	m_zkfpEng.PrintImageAt(long(hdc), x, y, width, height);

	ShowImageInCtrl(m_picCtrl2, EMPTY);//设置成空白图
	m_staticInfo.SetWindowText(ToWideChar(""));
}

//P48: 停止事件
void CFingerDlg::OnBnClickedDisconnect()
{
	m_zkfpEng.EndEngine();//停止指纹采集器
	GetDlgItem(IDC_BTN_CONNECT)->EnableWindow(TRUE);
}

//P50: 采集事件
void CFingerDlg::OnBnClickedBtnCapture()
{
	CString filename = L"capt.bmp";
	CString filter = L"所有文件(*.*)|*.*||";
	CFileDialog dlg(FALSE, NULL, filename, OFN_HIDEREADONLY, filter, this);
	if (dlg.DoModal() == IDOK)//判断是否按下"保存"按钮
	{
		CString str;
		str = dlg.GetPathName();
		m_zkfpEng.SaveBitmap(str);//保存图像
		MessageBox(str);
	}

}

//P51：获取图像参数信息(为后续处理) 
int ReadBMPImgFilePara(char* filename, long& width, long& height, long& depth)
{
	//载入图像
	CImage image;
	HRESULT hResult = image.Load(ToWideChar(filename));
	if (FAILED(hResult) || image.IsNull())//图像损坏或文件不存在
	{
		return -1;
	}

	//获得图像参数

	width = image.GetWidth();
	height = image.GetHeight();
	depth = image.GetBPP();

	if (depth != 8)
	{
		return -2;
	}

	//释放变量空间
	image.Destroy();

	return 0;
}

//P52：从图像文件读取数据CImage
int ReadBMPImgFileDataq(char* filename, unsigned char* data)
{
	//载入图像
	CImage image;
	HRESULT hResult = image.Load(ToWideChar(filename));
	if (FAILED(hResult) || image.IsNull())//图像损坏或文件不存在
	{
		return -1;
	}

	//获得图像参数

	long width = image.GetWidth();
	long height = image.GetHeight();
	long depth = image.GetBPP();

	if (depth != 8)
	{
		return -2;
	}

	//初始化结果数组
	memset(data, 0, width * height);

	//读取图像数据
	int pitch = image.GetPitch();
	unsigned char* pData1 = (unsigned char*)image.GetBits();
	unsigned char* pData2 = data;
	unsigned char gray = 0;

	unsigned char* pRow1, * pRow2, * pPix1, * pPix2;
	for (int y = 0; y < height; y++)
	{
		pRow1 = pData1 + pitch * y;
		pRow2 = pData2 + width * y;
		for (int x = 0; x < width; x++)
		{
			//获取原图像素的灰度值
			pPix1 = pRow1 + x;
			gray = *pPix1;
			//保存到结果数组
			pPix2 = pRow2 + x;
			*pPix2 = gray;
		}
	}
	image.Destroy();
	return 0;
}

//P55: 用文本文件保存数据(data无符号字符型数据数组)
int SaveDataToTextFile(char* dstFile, unsigned char* data, long width, long height)
{
	using namespace std;
	//打开目标文件
	ofstream fout(dstFile, ios::out);
	if (!fout)
	{
		return -1;
	}
	int space = 5;
	for (long i = 0; i < height * width; i++)
	{
		fout << setw(space) << int(data[i]);
		if (i % width == (width - 1))
		{
			fout << endl;
		}
	}
	fout.close();
	return 0;
}

//P56: 用文本文件保存数据(data浮点型数据数组)
int SaveDataToTextFile(char* dstFile, float* data, long width, long height)
{
	using namespace std;
	//打开目标文件
	ofstream fout(dstFile, ios::out);
	if (!fout)
	{
		return -1;
	}
	int preci = 6;
	int space = 16;
	fout.precision(preci);

	for (long i = 0; i < height * width; i++)
	{
		fout << setw(space) << data[i];
		if (i % width == (width - 1))
		{
			fout << endl;
		}
	}

	fout.close();

	return 0;
}

//P57: 载入图像单步测试
int Step1_LoadBmpImage(char* info)
{
	//选择图像
	//改进点：可以自由选择要打开的文件；
	CString filter = L"bmp文件(*.bmp)|*.bmp||";
	CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, filter);
	char filename[MAX_PATH] = { 0 };
	if (dlg.DoModal() == IDOK)//判断是否按下"打开"按钮
	{
		CString str = dlg.GetPathName();

		//将宽字符串转换成多字节字符串
		int num = WideCharToMultiByte(0, 0, str, -1, NULL, 0, NULL, false);
		char* chStr = new char[num];
		WideCharToMultiByte(0, 0, str, -1, chStr, num, NULL, false);

		strcpy(filename, chStr);
	}

	//复制图像
	CopyFile(ToWideChar(filename), ToWideChar(STEP_IMG_1), false);

	//读取图像参数并检测有效性
	long lWidth, lHeight, lDepth;
	int flag = ReadBMPImgFilePara(filename, lWidth, lHeight, lDepth);
	if (flag == -1)
	{
		sprintf(info, "图像加载失败,图像损坏或文件不存在");
		return -1;
	}
	else if (flag == -2) {
		sprintf(info, "图像加载失败,不是8位位图");
		return -1;
	}

	//读取图像数据
	unsigned char* data = new unsigned char[lWidth * lHeight];
	flag = ReadBMPImgFileDataq(filename, data);
	if (flag == -1)
	{
		sprintf(info, "图像加载失败,图像损坏或文件不存在");
		return -1;
	}
	else if (flag == -2) {
		sprintf(info, "图像加载失败,不是8位位图");
		return -1;
	}

	//保存数据
	flag = SaveDataToTextFile(STEP_TXT_1, data, lWidth, lHeight);
	if (flag != 0)
	{
		sprintf(info, "数据保存失败！");
		delete[] data;
		return -3;
	}
	//返回图像参数信息
	sprintf(info, "原图[%s],宽度[%d],高度[%d]，深度[%d b]", filename, lWidth, lHeight, lDepth);
	delete[] data;
	return  0;

}

//P61: 清空前一次保存的数据
bool DeleteEntireDir(const TCHAR* sPath, bool bDelDir)
{
	CFileFind ff;
	BOOL bFound;
	bFound = ff.FindFile(CString(sPath) + "\\*.*");
	while (bFound)
	{
		bFound = ff.FindNextFile();
		CString sFilePath = ff.GetFilePath();

		if (ff.IsDirectory())
		{
			if (!ff.IsDots())
			{
				DeleteEntireDir(sFilePath, bDelDir);
			}
		}
		else
		{
			if (ff.IsReadOnly())
			{
				SetFileAttributes(sFilePath, FILE_ATTRIBUTE_NORMAL);
			}
			DeleteFile(sFilePath);
		}
	}
	ff.Close();

	if (bDelDir)//是否删除该文件夹
	{
		SetFileAttributes(sPath, FILE_ATTRIBUTE_NORMAL);  //设置文件夹的属性  
		RemoveDirectory(sPath);
	}
	return true;
}

//P61: 清空前一次保存的数据
bool DeleteFolder(const TCHAR* pszFolderPath)
{
	if (_taccess(pszFolderPath, 0) == 0)
	{
		if (!DeleteEntireDir(pszFolderPath, false))
		{
			return false;
		}
	}
	return true;
}

//P57: 显示图像和参数信息	
void CFingerDlg::OnBnClickedBtnStep1()
{
	//初始化操作结果信息
	char info[MAX_PATH] = { 0 };

	//要清空的文件路径
	CString path1 = ToWideChar("D:\\C#\\Finger\\temp");
	if (DeleteFolder(path1))//判断保存前一次数据的文件是否清空
	{   //载入图像
		int i = Step1_LoadBmpImage(info);
		//显示操作结果信息
		m_staticInfo.SetWindowText(ToWideChar(info));
		//显示图像
		if (i == 0)//载入图像成功，显示图像；
		{
			ShowImageInCtrl(m_picCtrl1, STEP_IMG_1);
			ShowImageInCtrl(m_picCtrl2, EMPTY);//改进点P61(2)
			GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE);
		}
	}
	else
	{
		m_staticInfo.SetWindowText(ToWideChar("前一次数据没有清除成功！！！"));
		MessageBox(_T("前一次数据没有清除成功！！！"), _T("提示"));
	}

}

//P64: 获取指纹库统计信息
void GetDataBaseInfo(char* info)
{
	int pNo = 0;//指纹序号
	char name[MAX_PATH] = { 0 };//登记人姓名
	char srcFile[MAX_PATH] = { 0 };//指纹图像文件
	char mdlFile[MAX_PATH] = { 0 };//指纹特征文件
	FILE* index = fopen(DB_INDEX_TXT, "r");//打开索引文件
	while (!feof(index))
	{
		fscanf(index, "%d %s %s %s\n", &pNo, srcFile, mdlFile, name);
	}
	fclose(index);//关闭文件
	//统计指纹库信息
	sprintf(info, "当前指纹库中共有%d条记录", pNo);

}

void CFingerDlg::OnBnClickedBtnDatabase()
{
	// TODO: 在此添加控件通知处理程序代码
	char info[MAX_PATH] = { 0 };//初始化显示信息
	GetDataBaseInfo(info);//获取指纹库信息
	m_staticInfo.SetWindowText(ToWideChar(info));//显示指纹库信息
}

//P63: 创建并初始化指纹库 
void InitDatabase()
{
	_mkdir(TEMP_DIR);//创建临时文件夹
	_mkdir(DB_DIR);  //创建指纹库文件夹
	InitFile(DB_INDEX_TXT);//创建索引文件
}

//P63: 创建并初始化文件
void InitFile(char* filename)
{
	FILE* index = fopen(filename, "w");//若文件不存在则创建，若已经存在则清空内容
	fclose(index);//关闭文件
}

//P72: 从文本文件读取无符号字符类型的图像数据
int ReadDataFromTextFile(char* srcFile, unsigned char* data, long lWidth, long lHeight)
{
	//打开源文件
	using namespace std;
	ifstream fin(srcFile, ios::in);
	if (!fin) {
		return -1;
	}

	//读取数据
	int d = 0;
	for (long i = 0; i < lHeight * lWidth; i++)
	{
		fin >> d;
		data[i] = (unsigned char)d;
	}
	fin.close();
	return 0;
}

//P72: 从文本文件读取浮点数类型的其他数据P72
int ReadDataFromTextFile(char* srcFile, float* data, long lWidth, long lHeight)
{
	//打开源文件
	using namespace std;
	ifstream fin(srcFile, ios::in);
	if (!fin) {
		return -1;
	}

	//读取数据
	for (long i = 0; i < lHeight * lWidth; i++)
	{
		fin >> data[i];
	}
	fin.close();
	return 0;
}

//P78: 数组排序
void Sort(unsigned char* data, long dsize)
{
	unsigned char temp = 0;
	for (long i = 0; i < dsize; i++)
	{
		for (long j = dsize - 1; j > i; j--)
		{
			if (data[j] < data[j - 1])//升序排列
			{
				temp = data[j];
				data[j] = data[j - 1];
				data[j - 1] = temp;
			}
		}
	}
}
//P73: 中值滤波算法 
int MidFilter(unsigned char* ucImg, unsigned char* ucDstImg, long lWidth, long lHeight)
{
	//Step1:结果图像数据初始化
	memset(ucDstImg, 0, lWidth * lHeight);
	//Step2:中心区域滤波（使用3*3领域）
	unsigned char* pUp, * pDown, * pImg;//用3*3领域的3个图像数据指针
	unsigned char x[9];//3*3领域数组
	for (long i = 1; i < lHeight - 1; i++)
	{
		//初始化领域指针
		pUp = ucImg + (i - 1) * lWidth;
		pImg = ucImg + i * lWidth;
		pDown = ucImg + (i + 1) * lWidth;

		for (long j = 1; j < lWidth - 1; j++)//遍历第2列到倒数第2列
		{
			//移动领域指针
			pUp++;
			pImg++;
			pDown++;

			//获取3*3领域数据
			x[0] = *(pUp - 1);
			x[1] = *(pImg - 1);
			x[2] = *(pDown - 1);

			x[3] = *pUp;
			x[4] = *pImg;
			x[5] = *pDown;

			x[6] = *(pUp + 1);
			x[7] = *(pImg + 1);
			x[8] = *(pDown + 1);

			//数组排序
			Sort(x, 9);
			//结果图像数据取领域中值
			*(ucDstImg + i * lWidth + j) = x[4];
		}
	}

	//Step3:第1行和最后1行滤波（使用2*3领域）

	//第1行
	pDown = ucImg + lWidth;//领域指针初始化
	for (long j = 1; j < lWidth - 1; j++)
	{
		//获取2*3领域数据
		x[0] = *(ucImg + j - 1);
		x[1] = *(ucImg + j);
		x[2] = *(ucImg + j + 1);

		x[3] = *(pDown + j - 1);
		x[4] = *(pDown + j);
		x[5] = *(pDown + j + 1);


		//数组排序
		Sort(x, 6);

		//结果取中值
		*(ucDstImg + j) = x[3];

	}
	//最后1行(倒数第1行）
	pUp = ucImg + lWidth * (lHeight - 2);//领域指针初始化
	pDown = ucImg + lWidth * (lHeight - 1);//领域指针初始化
	for (long j = 1; j < lWidth - 1; j++)//最后1行遍历第2列到倒数第2列
	{
		//获取2*3领域数据

		x[0] = *(pDown + j - 1);
		x[1] = *(pDown + j);
		x[2] = *(pDown + j + 1);

		x[3] = *(pUp + j - 1);
		x[4] = *(pUp + j);
		x[5] = *(pUp + j + 1);

		//数组排序
		Sort(x, 6);

		//结果取中值
		*(ucDstImg + lWidth * (lHeight - 1) + j) = x[3];
	}
	//Step4:4个角点滤波（2*2领域）
	x[0] = *(ucImg);//获取2*2领域数据
	x[1] = *(ucImg + 1);
	x[2] = *(ucImg + lWidth);
	x[3] = *(ucImg + lWidth + 1);

	Sort(x, 4);
	*(ucDstImg) = x[2];//结果取中值

	//右上角点
	x[0] = *(ucImg + lWidth - 1);//获取2*2领域数据
	x[1] = *(ucImg + lWidth - 2);
	x[2] = *(ucImg + 2 * lWidth - 1);
	x[3] = *(ucImg + 2 * lWidth - 2);
	Sort(x, 4);
	*(ucDstImg + lWidth - 1) = x[2];//结果取中值


   //左下角点
	x[0] = *(ucImg + (lHeight - 1) * lWidth);//获取2*2领域数据
	x[1] = *(ucImg + (lHeight - 2) * lWidth);
	x[2] = *(ucImg + (lHeight - 1) * lWidth + 1);
	x[3] = *(ucImg + (lHeight - 2) * lWidth + 1);
	Sort(x, 4);
	*(ucDstImg + (lHeight - 1) * lWidth) = x[2];//结果取中值

	//右下角点
	x[0] = *(ucImg + (lHeight - 0) * lWidth - 1);//获取2*2领域数据
	x[1] = *(ucImg + (lHeight - 1) * lWidth - 1);
	x[2] = *(ucImg + (lHeight - 0) * lWidth - 2);
	x[3] = *(ucImg + (lHeight - 1) * lWidth - 2);
	Sort(x, 4);
	*(ucDstImg + (lHeight - 0) * lWidth - 1) = x[2];//结果取中值
	return 0;
}


//P82: 将位图数据写入BMP图像文件  
int WriteBMPImgFile(char* dstFileName, unsigned char** pusImgData) {
	//打开文件
	FILE* fp = fopen(dstFileName, "r+b");

	if (!fp) {
		return -1;
	}
	long IWidth, IHeight, imgType;//图像宽度 高度 深度
	long IStartPos = 0;//位图数据区起始位置
	fseek(fp, 10L, SEEK_SET);
	fread((char*)(&IStartPos), 4, 1, fp);//获取位图数据区起始位置（以字节为单位）
	fseek(fp, 18L, SEEK_SET);
	fread((char*)(&IWidth), 4, 1, fp);//获取图像宽度（以像素为单位）
	fread((char*)(&IHeight), 4, 1, fp);//获取图像高度（以像素为单位）
	unsigned short temp;
	fseek(fp, 28L, SEEK_SET);
	fread((char*)(&temp), 2, 1, fp);//获取图像深度（每个像素的位数，以位为单位）
	imgType = temp;
	if (imgType != 8) {//目前仅支持8为BMP位图图像
		return -2;
	}
	//向数据区写入数据
	unsigned char* usImgData = *pusImgData;//待保存数据数组地址
	long IWidthInFile = 0;//文件中每行像素 宽度、长度（以字节为单位）
	if (IWidth % 4 > 0) {
		IWidthInFile = IWidth - IWidth % 4 + 4;
	}
	else {
		IWidthInFile = IWidth;
	}
	for (int i = IHeight - 1; i >= 0; i--) {//从最后一行倒序存储
		fseek(fp, IStartPos, SEEK_SET);//定位行首地址
		fwrite((usImgData + i * IWidth), 1, IWidth, fp);//写入一行数据
		IStartPos += IWidthInFile;//调整行首地址

	}
	//关闭文件
	fclose(fp);
	return 0;
}

//P80: 保存数据到BMP文件  
int SaveDataToImageFile(char* srcFile, char* dstFile, unsigned char* data) {
	//文件复制
	CopyFile(ToWideChar(srcFile), ToWideChar(dstFile), false);
	//写入数据
	WriteBMPImgFile(dstFile, &data);
	return 0;
}

//P80: 保存数据到图像文件           			 
int SaveDataToImageFile(char* srcFile, char* dstFile, float* data, float scale) {
	//读取图像参数
	long IWidth, IHeight, IDepth;//图像宽度 高度 深度
	ReadBMPImgFilePara(srcFile, IWidth, IHeight, IDepth);

	//文件复制
	CopyFile(ToWideChar(srcFile), ToWideChar(dstFile), false);

	//数据转换
	unsigned char* tmpData = new unsigned char[IWidth * IHeight];
	for (int i = 0; i<int(IWidth * IHeight); i++) {
		tmpData[i] = unsigned char((scale * data[i]));
	}

	//写入数据
	WriteBMPImgFile(dstFile, &tmpData);
	//释放内存空间
	delete[] tmpData;
	return 0;
}

//P84：图像中值滤波单步测试 
int Step2_MidFilter(char* info)
{
	//设置输入文件名
	char srcTxtFile[MAX_PATH] = { STEP_TXT_1 };//源数据文件名
	char srcImgFile[MAX_PATH] = { STEP_IMG_1 };//源图文件名
	char dstTxtFile[MAX_PATH] = { STEP_TXT_2 };//结果数据文件名
	char dstImgFile[MAX_PATH] = { STEP_IMG_2 };//结果图文件名

	//读取图像参数
	long lWidth, lHeight, lDepth;//图像宽带、高度、深度
	ReadBMPImgFilePara(srcImgFile, lWidth, lHeight, lDepth);

	//读取源数据
	unsigned char* image1 = new unsigned char[lWidth * lHeight];
	ReadDataFromTextFile(srcTxtFile, image1, lWidth, lHeight);

	//中值滤波
	unsigned char* image2 = new unsigned char[lWidth * lHeight];
	MidFilter(image1, image2, lWidth, lHeight);

	//保存结果数据（TXT文件）
	SaveDataToTextFile(dstTxtFile, image2, lWidth, lHeight);
	//保存结果图像（BMP文件，仅用于显示）
	SaveDataToImageFile(srcImgFile, dstImgFile, image2);

	//释放空间
	delete[]image1;
	delete[]image2;

	//返回操作成败状态
	sprintf(info, "完成中值滤波。");
	return 0;
}

//P86:显示滤波处理结果
void CFingerDlg::OnBnClickedButton2()
{
	// TODO: 在此添加控件通知处理程序代码
	//初始化操作结果信息
	char info[MAX_PATH] = { 0 };
	//中值滤波
	Step2_MidFilter(info);
	//显示操作结果信息
	m_staticInfo.SetWindowTextW(ToWideChar(info));

	//显示图像
	ShowImageInCtrl(m_picCtrl1, STEP_IMG_1);//显示输入图像
	ShowImageInCtrl(m_picCtrl2, STEP_IMG_2);//显示输出图像
	ShowImageInCtrl(m_picCtrl3, STEP_IMG_2);

	GetDlgItem(IDC_BUTTON3)->EnableWindow(TRUE);
}

